/*******************************************************************************
 * Copyright (c) 2016 ARM Ltd. and others
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 * QNX Software Systems - initial API and implementation
 * Wind River Systems, Inc. - bug fixes
 * ARM Ltd and ARM Germany GmbH - application-specific implementation
 *******************************************************************************/
package com.arm.cmsis.parser;

import org.eclipse.jface.text.rules.ICharacterScanner;
import org.eclipse.jface.text.rules.IRule;
import org.eclipse.jface.text.rules.IToken;
import org.eclipse.jface.text.rules.Token;

/**
 * Recognizes positive integer numbers.
 */
public class NumberRule implements IRule {
	private IToken token;

	public NumberRule(IToken token) {
		super();
		this.token = token;
	}

	@Override
	public IToken evaluate(ICharacterScanner scanner) {
		scanner.unread();
		int prevCh = scanner.read();
		int startCh = scanner.read();
		int ch;
		int unreadCount = 1;

		if (isNumberStart(startCh)) {
			ch = startCh;
			if ((prevCh >= 'A' && prevCh <= 'Z') || (prevCh >= 'a' && prevCh <= 'z') || prevCh == '_') {
				while (isValidIdentifierChar((char) ch)) {
					ch = scanner.read();
				}
				return Token.UNDEFINED;
			}
			if (startCh == '-') {
				ch = scanner.read();
				++unreadCount;
			}
			if (ch == '0') {
				int xCh = scanner.read();
				if (xCh == 'x' || xCh == 'X' || xCh == 'b' || xCh == 'B') {
					// hex/bin number
					do {
						ch = scanner.read();
					} while (isHexNumberPart((char) ch));
					scanner.unread();
					if (!isValidIdentifierChar((char) ch)) {
						return token;
					}
					return Token.UNDEFINED;
				}
				scanner.unread();
				// assert ch == '0';
			} else if (ch == '.') {
				ch = scanner.read();
				++unreadCount;
			}
			if (Character.isDigit((char) ch)) {
				// need at least one digit
				do {
					ch = scanner.read();
				} // while (Character.isDigit((char)ch));
				while (isHexNumberPart((char) ch));

				if (!isNumberSuffix((char) ch)) {
					scanner.unread();
				}
				return token;
			}
		}
		do {
			scanner.unread();
		} while (--unreadCount > 0);
		return Token.UNDEFINED;
	}

	/**
	 * Checks if start of number.
	 *
	 * @param ch
	 * @return true if it is number start.
	 */
	private boolean isNumberStart(int ch) {
		return ch == '-' || Character.isDigit((char) ch);
	}

	/**
	 * Checks if part of hex number;
	 *
	 * @param ch
	 * @return true if it is part of a hex number
	 */
	private boolean isHexNumberPart(int ch) {
		return Character.isDigit((char) ch) || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F');
	}

	private boolean isNumberSuffix(char ch) {
		return ch == 'b' || ch == 'B' || ch == 'h' || ch == 'H' || ch == 'd' || ch == 'D' || ch == 'o' || ch == 'O'
				|| ch == 'q' || ch == 'Q';
	}

	private boolean isValidIdentifierChar(char ch) {
		return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') || ch == '_';
	}
}